﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MainForm.cs" company="YellowFish">
//   Copyright © YellowFish 2009
// </copyright>
// <summary>
//   Defines the MainForm type.
// </summary>
// <author>
// Roman "4ux-nbIx" Novitsky
// </author>
// --------------------------------------------------------------------------------------------------------------------

namespace OneNoteNotify
{
  #region Imported Namespaces


  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Drawing;
  using System.IO;
  using System.Windows.Forms;

  using Properties;

  using YF.Controls;
  using YF.OneNote;
  using YF.OneNote.DataTypes;
  using YF.OneNoteNotify.Data;


  #endregion


  /// <summary>
  /// Describes and manipulates the main form.
  /// </summary>
  public partial class MainForm: Form
  {
    #region Constants and Fields


    private readonly string _stateFile;
    private readonly List<NotesUpdate> _updates = new List<NotesUpdate>();
    private Color _defaultNodeBackColor;
    private NoteTreeNode _treeNodes;


    #endregion


    #region Constructors and Destructors


    /// <summary>
    /// Initializes a new instance of the <see cref="MainForm"/> class.
    /// </summary>
    public MainForm()
    {
      InitializeComponent();

      if (Settings.Default.HideToTrayOnStartup)
      {
        WindowState = FormWindowState.Minimized;
        ShowInTaskbar = false;
      }

      _trayIcon.Icon = Resources.TrayIcon;
      _trayIcon.DoubleClick += TrayIconDoubleClick;

      _stateFile = Application.UserAppDataPath + "\\state.xml";

      LoadState();

      _smallImages.Images.Add("OneNote", Resources.OneNoteIconSmall);
      _smallImages.Images.Add("SendToOneNote", Resources.SendToOneNoteSmall);

      InitializeTree();

      NoteTreeNode.Changed += NoteTreeNode_Changed;
      NoteTreeNode.MarkedAsRead += NoteTreeNode_MarkedAsRead;
      NoteTreeNode.MarkedAsUnread += NoteTreeNode_MarkedAsUnread;

      _updateTimer.Start();

      _notesTree.NodeMouseDoubleClick += NotesTreeNodeMouseDoubleClick;
    }


    #endregion


    #region Methods


    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Form.Closing"></see> event.
    /// </summary>
    /// <param name="e">
    /// A <see cref="T:System.ComponentModel.CancelEventArgs"></see> that contains the event data. 
    /// </param>
    protected override void OnClosing(CancelEventArgs e)
    {
      var eventArgs = e as FormClosingEventArgs;

      if (_trayIcon.Visible && eventArgs != null && eventArgs.CloseReason == CloseReason.UserClosing)
      {
        Hide();
        e.Cancel = true;
      }

      base.OnClosing(e);
    }


    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Form.Load"></see> event.
    /// </summary>
    /// <param name="e">
    /// An <see cref="T:System.EventArgs"></see> that contains the event data. 
    /// </param>
    protected override void OnLoad(EventArgs e)
    {
      base.OnLoad(e);

      GetUpdates();
    }


    private static NoteTreeNode CreateNodes(NotesNode node)
    {
      if (node is Page)
      {
        var page = (Page)node;

        if (!page.HasContents)
        {
          page.UpdateContents();
        }
      }

      var treeNode = new NoteTreeNode(node);

      foreach (NotesNode child in node.Children)
      {
        TreeNode childTreeNode = CreateNodes(child);
        treeNode.Nodes.Add(childTreeNode);
      }

      return treeNode;
    }


    private static NoteTreeNode FilterNodes(NoteTreeNode node)
    {
      if (node.Unread)
      {
        var filteredNode = new NoteTreeNode(node.NotesItem);

        foreach (NoteTreeNode childNode in node.Nodes)
        {
          NoteTreeNode filteredChildNode = FilterNodes(childNode);

          if (filteredChildNode != null)
          {
            filteredNode.Nodes.Add(filteredChildNode);
          }
        }

        return filteredNode;
      }

      return null;
    }


    private static void MarkUpdatedNodes(NotesUpdate update, bool read)
    {
      foreach (NoteTreeNode node in update.Nodes)
      {
        if (node.NotesItem is Page)
        {
          node.Unread = read;
        }
      }
    }


    private static void NavigateTo(NoteTreeNode node)
    {
      var note = node.NotesItem as NotesHierarchyNode;

      if (note == null)
      {
        return;
      }

      note.NavigateTo(Settings.Default.OpenItemsInNewWindow);

      if (Settings.Default.MarkNodeAsReadOnOpeningInOneNote)
      {
        node.Unread = false;
      }
    }


    private static void NotesTreeNodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
    {
      var node = (NoteTreeNode)e.Node;

      NavigateTo(node);
    }


    private static void ShowOptionsDialog()
    {
      var dialog = new SettingsDialog(Settings.Default);
      DialogResult result = dialog.ShowDialog();

      switch (result)
      {
        case DialogResult.OK:
          Settings.Default.Save();
          break;
        case DialogResult.Cancel:
          Settings.Default.Reload();
          break;
      }
    }


    private static void UpdateChangedNodesContents(NotesUpdate update)
    {
      foreach (NoteTreeNode node in update.Nodes)
      {
        // if(!Settings.Default.MonitoringSettings.IsMonitoredNode(node.NotesItem.ID))
        // continue;
        List<INotesChange> changes = node.GetLastUpdateChanges();

        foreach (INotesChange change in changes)
        {
          if (change is NotesPropertyChange)
          {
            var propertyChange = (NotesPropertyChange)change;
            if (propertyChange.PropertyName == "LastModifiedTime")
            {
              var oldTime = (DateTime)propertyChange.OldValue;
              var newTime = (DateTime)propertyChange.NewValue;

              TimeSpan timeSpan = newTime - oldTime;

              if (timeSpan.Minutes > 5)
              {
                node.UpdateContents();
                break;
              }
            }
          }
        }
      }
    }


    private void CloseContextMenuItemClick(object sender, EventArgs e)
    {
      CloseApplication();
    }


    private void NotesTreeNodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
    {
      if (e.Button == MouseButtons.Right)
      {
        _notesTree.SelectedNode = e.Node;
      }
    }


    private void OpenContextMenuItemClick(object sender, EventArgs e)
    {
      ShowWindow();
    }


    private void OptionsContextMenuItemClick(object sender, EventArgs e)
    {
      ShowOptionsDialog();
    }


    private void OptionsMenuItemClick(object sender, EventArgs e)
    {
      ShowOptionsDialog();
    }


    private void TrayIconDoubleClick(object sender, EventArgs e)
    {
      ShowWindow();
    }


    private void TreeContextMenuOpening(object sender, CancelEventArgs e)
    {
      var contextMenu = (ContextMenuStrip)sender;
      var node = (NoteTreeNode)_notesTree.SelectedNode;
      e.Cancel = false;

      if (node.Unread)
      {
        contextMenu.Items["markAsRead"].Visible = true;
        contextMenu.Items["markAsUnread"].Visible = false;
      }
      else
      {
        contextMenu.Items["markAsRead"].Visible = false;
        if (node.NotesItem is Page)
        {
          contextMenu.Items["markAsUnread"].Visible = true;
        }
        else
        {
          e.Cancel = true;
        }
      }
    }


    private void UpdateContextMenuItemClick(object sender, EventArgs e)
    {
      GetUpdates();
    }


    private void ClearToolStripMenuItemClick(object sender, EventArgs e)
    {
      _treeNodes.MarkAllAsRead();
    }


    private void CloseApplication()
    {
      DialogResult dialogResult = DialogResult.Yes;

      if (!Settings.Default.DisableQuitConfirmation)
      {
        const string text = "Are you sure you want to quit?";
        const string caption = "Quit?";
        dialogResult = MessageBox.Show(text, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question, 
                                       MessageBoxDefaultButton.Button2);
      }

      switch (dialogResult)
      {
        case DialogResult.Yes:
          _trayIcon.Visible = false;
          break;
      }

      Close();
    }


    private void GetUpdates()
    {
      var update = new NotesUpdate();
      _updates.Add(update);

      OneNote.Update();

      bool hasChanges = update.HasChanges();

      if (hasChanges)
      {
        UpdateChangedNodesContents(update);

        if (Settings.Default.ShowBallonTip && !ContainsFocus)
        {
          _trayIcon.ShowBalloonTip();
        }

        if (Settings.Default.MarkAllAsReadOnUpdate)
        {
          if (_updates.Count > 1)
          {
            NotesUpdate previous = _updates[_updates.Count - 2];
            MarkUpdatedNodes(previous, false);
          }
        }

        MarkUpdatedNodes(update, true);

        SaveState();

        GC.Collect();
        GC.WaitForPendingFinalizers();
      }
    }


    private void InitializeTree()
    {
      Notes hierarchy = OneNote.Notes;

      _treeNodes = CreateNodes(hierarchy);
      _treeNodes.ImageKey = "OneNote";
      _defaultNodeBackColor = _treeNodes.BackColor;

      _notesTree.Nodes.Add(_treeNodes);
    }


    private void LoadState()
    {
      try
      {
        if (File.Exists(_stateFile))
        {
          OneNote.LoadState(_stateFile);
        }
      }
      catch (Exception e)
      {
        MessageBox.Show("Failed to load state. \n\n" + e.Message + "\n\nStack Trace:\n" + e.StackTrace, 
                        "An error occurred!", MessageBoxButtons.OK, MessageBoxIcon.Error);
      }
    }


    private void MarkAsReadClick(object sender, EventArgs e)
    {
      var node = (NoteTreeNode)_notesTree.SelectedNode;
      node.Unread = false;
    }


    private void MarkAsUnreadClick(object sender, EventArgs e)
    {
      var node = (NoteTreeNode)_notesTree.SelectedNode;
      node.Unread = true;
    }


    private void NavigateToClick(object sender, EventArgs e)
    {
      var node = (NoteTreeNode)_notesTree.SelectedNode;
      NavigateTo(node);
    }


    private void NoteTreeNode_Changed(object sender, EventArgs e)
    {
      var node = (NoteTreeNode)sender;

      NotesUpdate lastUpdate = _updates[_updates.Count - 1];
      lastUpdate.Nodes.Add(node);
    }


    private void NoteTreeNode_MarkedAsRead(object sender, EventArgs e)
    {
      var node = (NoteTreeNode)sender;

      node.BackColor = _defaultNodeBackColor;

      if (node.Parent != null && node.Parent.AreAllChildrenRead())
      {
        node.Parent.Unread = false;
      }

      if (node == _treeNodes)
      {
        _trayIcon.Icon = Resources.TrayIcon;
      }
    }


    private void NoteTreeNode_MarkedAsUnread(object sender, EventArgs e)
    {
      var node = (NoteTreeNode)sender;
      node.BackColor = Color.Yellow;

      NoteTreeNode parent = node.Parent;

      if (parent != null)
      {
        parent.Unread = true;
      }

      if (node == _treeNodes)
      {
        _trayIcon.Icon = Resources.TrayNewChangesIcon;
      }
    }


    private void QuitToolStripMenuItemClick(object sender, EventArgs e)
    {
      CloseApplication();
    }


    private void SaveState()
    {
      try
      {
        OneNote.SaveState(_stateFile);
      }
      catch (Exception e)
      {
        MessageBox.Show("Failed to save state. \n\n" + e.Message + "\n\nStack Trace:\n" + e.StackTrace, 
                        "An error occurred!", MessageBoxButtons.OK, MessageBoxIcon.Error);
      }
    }


    private void ShowOnlyUpdatedItemsMenuItemClick(object sender, EventArgs e)
    {
      var menuItem = (ToolStripMenuItem)sender;

      menuItem.Checked = !menuItem.Checked;

      UpdateView(menuItem.Checked);
    }


    private void ShowWindow()
    {
      ShowInTaskbar = true;
      WindowState = FormWindowState.Normal;
      Show();
    }


    private void UpdateMenuItemClick(object sender, EventArgs e)
    {
      GetUpdates();
    }


    private void UpdateTimerTick(object sender, EventArgs e)
    {
      GetUpdates();
    }


    private void UpdateView(bool showOnlyUpdated)
    {
      if (showOnlyUpdated)
      {
        _notesTree.Nodes.Clear();

        NoteTreeNode filteredNodes = FilterNodes(_treeNodes);

        if (filteredNodes != null)
        {
          _notesTree.Nodes.Add(filteredNodes);
        }
      }
      else
      {
        _notesTree.Nodes.Clear();
        _notesTree.Nodes.Add(_treeNodes);
      }
    }


    #endregion
  }
}